/**
 * Defines a request to switch the foreground Android user.
 *
 * This property is used primarily by the Android System to inform the HAL that the
 * current foreground Android user is switching, but it could also be used by the HAL to request
 * the Android system to switch users - the
 *
 * When the request is made by Android, it sets a VehiclePropValue and the HAL must responde
 * with a property change event; when the HAL is making the request, it must also do it through
 * a property change event (the main difference is that the request id will be positive in the
 * former case, and negative in the latter; the SwitchUserMessageType will also be different).
 *
 * The format of both request is defined by SwitchUserRequest and the format of the response
 * (when needed) is defined by SwitchUserResponse. How the HAL (or Android System) should
 * proceed depends on the message type (which is defined by the SwitchUserMessageType
 * parameter), as defined below.
 *
 * 1.LEGACY_ANDROID_SWITCH
 * -----------------------
 *
 * Called by the Android System to indicate the Android user is about to change, when the change
 * request was made in a way that is not integrated with the HAL (for example, through
 * adb shell am switch-user).
 *
 * The HAL can switch its internal user once it receives this request, but it doesn't need to
 * reply back to the Android System. If its internal user cannot be changed for some reason,
 * then it must wait for the SWITCH_USER(type=ANDROID_POST_SWITCH) call to recover
 * (for example, it could issue a SWITCH_USER(type=VEHICLE_REQUEST) to switch back to
 * the previous user), but ideally it should never fail (as switching back could result in a
 * confusing experience for the end user).
 *
 * For example, if the system have users (0, 10, 11) and it's switching from 0 to 11 (where none
 * of them have any special flag), the request would be:
 *
 * int32[0]:  42  // request id
 * int32[1]:  1   // SwitchUserMessageType::LEGACY_ANDROID_SWITCH
 * int32[2]:  11  // target user id
 * int32[3]:  0   // target user flags (none)
 * int32[4]:  10  // current user
 * int32[5]:  0   // current user flags (none)
 * int32[6]:  3   // number of users
 * int32[7]:  0   // user #0 (Android user id 0)
 * int32[8]:  0   // flags of user #0 (none)
 * int32[9]:  10  // user #1 (Android user id 10)
 * int32[10]: 0   // flags of user #1 (none)
 * int32[11]: 11  // user #2 (Android user id 11)
 * int32[12]: 0   // flags of user #2 (none)
 *
 * 2.ANDROID_SWITCH
 * ----------------
 * Called by the Android System to indicate the Android user is about to change, but Android
 * will wait for the HAL's response (up to some time) before proceeding.
 *
 * The HAL must switch its internal user once it receives this request, then respond back to
 * Android with a SWITCH_USER(type=VEHICLE_RESPONSE) indicating whether its internal
 * user was switched or not (through the SwitchUserStatus enum).
 *
 * For example, if Android has users (0, 10, 11) and it's switching from 10 to 11 (where
 * none of them have any special flag), the request would be:
 *
 * int32[0]:  42  // request id
 * int32[1]:  2   // SwitchUserMessageType::ANDROID_SWITCH
 * int32[2]:  11  // target user id
 * int32[3]:  0   // target user flags (none)
 * int32[4]:  10  // current user
 * int32[5]:  0   // current user flags (none)
 * int32[6]:  3   // number of users
 * int32[7]:  0   // 1st user (user 0)
 * int32[8]:  1   // 1st user flags (SYSTEM)
 * int32[9]:  10  // 2nd user (user 10)
 * int32[10]: 0   // 2nd user flags (none)
 * int32[11]: 11  // 3rd user (user 11)
 * int32[12]: 0   // 3rd user flags (none)
 *
 * If the request succeeded, the HAL must update the propery with:
 *
 * int32[0]: 42  // request id
 * int32[1]: 3   // messageType = SwitchUserMessageType::VEHICLE_RESPONSE
 * int32[2]: 1   // status = SwitchUserStatus::SUCCESS
 *
 * But if it failed, the response would be something like:
 *
 * int32[0]: 42   // request id
 * int32[1]: 3    // messageType = SwitchUserMessageType::VEHICLE_RESPONSE
 * int32[2]: 2    // status = SwitchUserStatus::FAILURE
 * string: "108-D'OH!" // OEM-spefic error message
 *
 * 3.VEHICLE_RESPONSE
 * ------------------
 * Called by the HAL to indicate whether a request of type ANDROID_SWITCH should proceed or
 * abort - see the ANDROID_SWITCH section above for more info.
 *
 * 4.VEHICLE_REQUEST
 * ------------------
 * Called by the HAL to request that the current foreground Android user is switched.
 *
 * This is useful in situations where Android started as one user, but the vehicle identified
 * the driver as another user. For example, user A unlocked the car using the key fob of user B;
 * the INITIAL_USER_INFO request returned user B, but then a face recognition subsubsystem
 * identified the user as A.
 *
 * The HAL makes this request by a property change event (passing a negative request id), and
 * the Android system will response by issue an ANDROID_POST_SWITCH call which the same
 * request id.
 *
 * For example, if the current foreground Android user is 10 and the HAL asked it to switch to
 * 11, the request would be:
 *
 * int32[0]: -108  // request id
 * int32[1]: 4     // messageType = SwitchUserMessageType::VEHICLE_REQUEST
 * int32[2]: 11    // Android user id
 *
 * If the request succeeded and Android has 3 users (0, 10, 11), the response would be:
 *
 * int32[0]: -108 // request id
 * int32[1]:  5   // messageType = SwitchUserMessageType::ANDROID_POST_SWITCH
 * int32[2]:  11  // target user id
 * int32[3]:  0   // target user id flags (none)
 * int32[4]:  11  // current user
 * int32[5]:  0   // current user flags (none)
 * int32[6]:  3   // number of users
 * int32[7]:  0   // 1st user (user 0)
 * int32[8]:  0   // 1st user flags (none)
 * int32[9]:  10  // 2nd user (user 10)
 * int32[10]: 4   // 2nd user flags (none)
 * int32[11]: 11  // 3rd user (user 11)
 * int32[12]: 3   // 3rd user flags (none)
 *
 * Notice that both the current and target user ids are the same - if the request failed, then
 * they would be different (i.e, target user would be 11, but current user would still be 10).
 *
 * 5.ANDROID_POST_SWITCH
 * ---------------------
 * Called by the Android System after a request to switch a user was made.
 *
 * This property is called after switch requests of any type (i.e., LEGACY_ANDROID_SWITCH,
 * ANDROID_SWITCH, or VEHICLE_REQUEST) and can be used to determine if the request succeeded or
 * failed:
 *
 * 1. When it succeeded, it's called when the Android user is in the unlocked state and the
 *    value of the current and target users ids in the response are the same. This would be
 *    equivalent to receiving an Intent.ACTION_USER_UNLOCKED in an Android app.
 * 2. When it failed it's called right away and the value of the current and target users ids
 *    in the response are different (as the current user didn't change to the target).
 * 3. If a new switch request is made before the HAL responded to the previous one or before
 *    the user was unlocked, then the ANDROID_POST_SWITCH request is not made. For example,
 *    the driver could accidentally switch to the wrong user which has lock credentials, then
 *    switch to the right one before entering the credentials.
 *
 * The HAL can update its internal state once it receives this request, but it doesn't need to
 * reply back to the Android System.
 *
 * Request: the first N values as defined by INITIAL_USER_INFO (where the request-specific
 * value at index 1 is SwitchUserMessageType::ANDROID_POST_SWITCH), then 2 more values for the
 * target user id (i.e., the Android user id that was requested to be switched to) and its flags
 * (as defined by  UserFlags).
 *
 * Response: none.
 *
 * Example: see VEHICLE_REQUEST section above.
 *
 * @change_mode VehiclePropertyChangeMode:ON_CHANGE
 * @access VehiclePropertyAccess:READ_WRITE
 */

#include "PlatformProperty.h"
#include "UserHalHelper.h"

using namespace user_hal_helper;

SYSTEM_READ_WRITE_PROPERTY(SWITCH_USER, MixedValue) {
    builder.area(VehicleArea::GLOBAL)
        .initValue([]() {
            MixedValue ret = {
                .int32Values = {0},
                .floatValues = {0.0f},
                .int64Values = {0},
                .bytes = {0x00},
                .stringValue = "SWITCH_USER",
            };
            return ret;
        })
        .immutableStatus(PropertyStatus::Active)
        .withValueBySignals([](const MixedValue &value) {
            return value;
        })
        .onSetProperty([this](const MixedValue &value) {
            auto switchUserRequest = toSwitchUserRequest(value);
            if (!switchUserRequest.ok()) {
                ALOGE("SWITCH_USER: toSwitchUserRequest failed: %s", switchUserRequest.error().message().c_str());
                return;
            }
            if (switchUserRequest->requestId == INVALID_REQUEST_ID) {
                ALOGE("SWITCH_USER: switchUserRequest requestId is invalid -1");
                return;
            }
            ALOGD("SWITCH_USER: switchUserRequest:= %s", toString(switchUserRequest.value()).c_str());

            std::string stringPidToUids = this->getPlatformProfile()->getAuxiliary()->getString(PROFILE_ID_TO_USER_ID, "");
            SwitchUserMessageType messageType = switchUserRequest->messageType;
            int32_t targetProfileId = getProfileId(stringPidToUids, switchUserRequest->targetUser.userId);
            int32_t currentProfileId = getCurrentProfileID();
            ALOGD("SWITCH_USER: targetProfileId=%d, currentProfileId=%d", targetProfileId, currentProfileId);

            // LEGACY_ANDROID_SWITCH类型, VHAL无需回应FWK直接执行切换即可
            if (messageType == SwitchUserMessageType::LEGACY_ANDROID_SWITCH) {
                if (targetProfileId != INVALID_PROFILE_ID) {
                    if (targetProfileId != currentProfileId && this->getPlatformProfile()->isCreated(targetProfileId)) {
                        this->getPlatformProfile()->activeProfile(targetProfileId);
                    }
                }
            // ANDROID_SWITCH类型，如果FWK与VHAL的用户不一致，VHAL需要切换至FWK的用户
            } else if (messageType == SwitchUserMessageType::ANDROID_SWITCH) {
                if (targetProfileId != INVALID_PROFILE_ID) {
                    SwitchUserResponse switchUserResponse = {
                        .requestId = switchUserRequest->requestId,
                        .messageType = SwitchUserMessageType::VEHICLE_RESPONSE,
                        .status = SwitchUserStatus::FAILURE,
                    };
                    if (targetProfileId != currentProfileId) {
                        bool result = this->getPlatformProfile()->activeProfile(targetProfileId);
                        if (currentProfileId == EPHEMERAL_PROFILE_ID && result) {
                            stringPidToUids = removeRelationPidToUidsString(EPHEMERAL_PROFILE_ID, stringPidToUids);
                        }
                        switchUserResponse.status = result ? SwitchUserStatus::SUCCESS : SwitchUserStatus::FAILURE;
                    } else {
                        switchUserResponse.status = SwitchUserStatus::SUCCESS;
                    }
                    ALOGD("SWITCH_USER: switchUserResponse(VEHICLE_RESPONSE):= %s", toString(switchUserResponse).c_str());
                    setValue(static_cast<int32_t>(VehicleArea::GLOBAL), toVehiclePropValue(switchUserResponse)->value);
                }
            // ANDROID_POST_SWITCH类型，如果FWK与VHAL的用户不一致，FWK需要切换至VHAL的用户
            } else if (messageType == SwitchUserMessageType::ANDROID_POST_SWITCH) {
                int32_t currentUserProfileId = getProfileId(stringPidToUids, switchUserRequest->usersInfo.currentUser.userId);
                if (currentUserProfileId != currentProfileId) {
                    int32_t userId = getUserId(stringPidToUids, currentProfileId);
                    if (userId != INVALID_USER_ID) {
                        SwitchUserRequest switchUserRequest = {
                            .requestId = -100,
                            .messageType = SwitchUserMessageType::VEHICLE_REQUEST,
                            .targetUser.userId = userId,
                        };
                        ALOGD("SWITCH_USER: switchUserRequest(VEHICLE_REQUEST):= %s", toString(switchUserRequest).c_str());
                        setValue(static_cast<int32_t>(VehicleArea::GLOBAL), toVehiclePropValue(switchUserRequest)->value);
                    }
                }
            }

            this->getPlatformProfile()->getAuxiliary()->setString(PROFILE_ID_TO_USER_ID, stringPidToUids);
            this->getPlatformProfile()->getAuxiliary()->save();
        });
}